hitloop 0.0.0-stage → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (469) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +218 -2
  3. package/dist/agent.js +318 -0
  4. package/dist/cli.js +897 -0
  5. package/dist/client/assets/app.css +1 -0
  6. package/dist/client/assets/chunks/abap-EINPUMCJ.js +1 -0
  7. package/dist/client/assets/chunks/abnfDiagram-S7VZAUFO-W2CM2W6D.js +1 -0
  8. package/dist/client/assets/chunks/actionscript-3-O776AQUO.js +1 -0
  9. package/dist/client/assets/chunks/ada-WLOTXRTV.js +1 -0
  10. package/dist/client/assets/chunks/ahk-EF37A2DY.js +1 -0
  11. package/dist/client/assets/chunks/ahk2-FH4K3CKT.js +1 -0
  12. package/dist/client/assets/chunks/angular-html-ZALQFV2H.js +1 -0
  13. package/dist/client/assets/chunks/angular-ts-4IWMF7QO.js +1 -0
  14. package/dist/client/assets/chunks/apache-44AYOEWM.js +1 -0
  15. package/dist/client/assets/chunks/apex-XRYPID42.js +1 -0
  16. package/dist/client/assets/chunks/apl-5HBALFOI.js +1 -0
  17. package/dist/client/assets/chunks/applescript-2DZEHZEC.js +1 -0
  18. package/dist/client/assets/chunks/ara-ZGZQBLXL.js +1 -0
  19. package/dist/client/assets/chunks/architecture-WOLXFQ4H-EJGENNRL.js +1 -0
  20. package/dist/client/assets/chunks/architectureDiagram-YGSQEFNW-2VHWSKO6.js +36 -0
  21. package/dist/client/assets/chunks/asciidoc-GSQAKCPO.js +1 -0
  22. package/dist/client/assets/chunks/asm-BFURM5BK.js +1 -0
  23. package/dist/client/assets/chunks/astro-VJORWDZJ.js +1 -0
  24. package/dist/client/assets/chunks/awk-BSHTWXXX.js +1 -0
  25. package/dist/client/assets/chunks/ballerina-HAOQJ4B2.js +1 -0
  26. package/dist/client/assets/chunks/bat-OSH45EOF.js +1 -0
  27. package/dist/client/assets/chunks/beancount-RBATIHOV.js +1 -0
  28. package/dist/client/assets/chunks/berry-CZUIOUM4.js +1 -0
  29. package/dist/client/assets/chunks/bibtex-KCL2UWHE.js +1 -0
  30. package/dist/client/assets/chunks/bicep-SZJZB3SG.js +1 -0
  31. package/dist/client/assets/chunks/bird2-XBF5YQQV.js +1 -0
  32. package/dist/client/assets/chunks/blade-Q24LEVFC.js +1 -0
  33. package/dist/client/assets/chunks/blockDiagram-APKKGZFW-53LXSOOT.js +140 -0
  34. package/dist/client/assets/chunks/bpmnIcons-APHXDYXF-FMBRQNTY.js +1 -0
  35. package/dist/client/assets/chunks/bsl-TI44NWFB.js +1 -0
  36. package/dist/client/assets/chunks/c-NLHY4Y4N.js +1 -0
  37. package/dist/client/assets/chunks/c3-BUTEM6PO.js +1 -0
  38. package/dist/client/assets/chunks/c4Diagram-KJ2TOQU5-O53G4GQH.js +38 -0
  39. package/dist/client/assets/chunks/cadence-JIRTEU64.js +1 -0
  40. package/dist/client/assets/chunks/cairo-3W6NVDNW.js +1 -0
  41. package/dist/client/assets/chunks/chapel-BPBJ2E7K.js +1 -0
  42. package/dist/client/assets/chunks/chunk-25KVF5ZE.js +1 -0
  43. package/dist/client/assets/chunks/chunk-2CJKSQOI.js +1 -0
  44. package/dist/client/assets/chunks/chunk-2QIJRTWR.js +1 -0
  45. package/dist/client/assets/chunks/chunk-2ZCRJZGF.js +2 -0
  46. package/dist/client/assets/chunks/chunk-3ILNDZNY.js +213 -0
  47. package/dist/client/assets/chunks/chunk-3M2OOQ7S.js +1 -0
  48. package/dist/client/assets/chunks/chunk-3M57NTNK.js +1 -0
  49. package/dist/client/assets/chunks/chunk-3MR6DCZJ.js +1 -0
  50. package/dist/client/assets/chunks/chunk-3WGHS7F7.js +1 -0
  51. package/dist/client/assets/chunks/chunk-4JK75V33.js +1 -0
  52. package/dist/client/assets/chunks/chunk-4NXNVEWK.js +1 -0
  53. package/dist/client/assets/chunks/chunk-4RNNBDFQ.js +1 -0
  54. package/dist/client/assets/chunks/chunk-4T4NVH7V.js +1 -0
  55. package/dist/client/assets/chunks/chunk-4TFFBC35.js +1 -0
  56. package/dist/client/assets/chunks/chunk-4WYBE67P.js +1 -0
  57. package/dist/client/assets/chunks/chunk-56ITEUFA.js +1 -0
  58. package/dist/client/assets/chunks/chunk-5DXRE7IH.js +1 -0
  59. package/dist/client/assets/chunks/chunk-5IMADCNU.js +1 -0
  60. package/dist/client/assets/chunks/chunk-5XZPGKN4.js +1 -0
  61. package/dist/client/assets/chunks/chunk-6O32UKSP.js +1 -0
  62. package/dist/client/assets/chunks/chunk-7BAEB64Q.js +1 -0
  63. package/dist/client/assets/chunks/chunk-7FMWBHZO.js +1 -0
  64. package/dist/client/assets/chunks/chunk-7GFDITUT.js +1 -0
  65. package/dist/client/assets/chunks/chunk-7H4MLBHI.js +1 -0
  66. package/dist/client/assets/chunks/chunk-7OJ4MEEB.js +1 -0
  67. package/dist/client/assets/chunks/chunk-7RSEISFE.js +1 -0
  68. package/dist/client/assets/chunks/chunk-7YNZZQVT.js +1 -0
  69. package/dist/client/assets/chunks/chunk-AB7TIL2G.js +1 -0
  70. package/dist/client/assets/chunks/chunk-ADDF76FL.js +1 -0
  71. package/dist/client/assets/chunks/chunk-AF6UTCD4.js +321 -0
  72. package/dist/client/assets/chunks/chunk-AJ2K6ASU.js +1 -0
  73. package/dist/client/assets/chunks/chunk-AUBAENQZ.js +1 -0
  74. package/dist/client/assets/chunks/chunk-BJEYVZ65.js +1 -0
  75. package/dist/client/assets/chunks/chunk-BLRM6LXA.js +1 -0
  76. package/dist/client/assets/chunks/chunk-BOCTYCDZ.js +1 -0
  77. package/dist/client/assets/chunks/chunk-BU4VRYN7.js +1 -0
  78. package/dist/client/assets/chunks/chunk-CALYHV7D.js +1 -0
  79. package/dist/client/assets/chunks/chunk-CF4W3RZ4.js +1 -0
  80. package/dist/client/assets/chunks/chunk-DCZ2OBSL.js +1 -0
  81. package/dist/client/assets/chunks/chunk-DUFPBTHD.js +1 -0
  82. package/dist/client/assets/chunks/chunk-DUICIQBC.js +88 -0
  83. package/dist/client/assets/chunks/chunk-EH2N475V.js +1 -0
  84. package/dist/client/assets/chunks/chunk-EHLLNPZT.js +1 -0
  85. package/dist/client/assets/chunks/chunk-EIBFAW37.js +1 -0
  86. package/dist/client/assets/chunks/chunk-EO3ER3GN.js +1 -0
  87. package/dist/client/assets/chunks/chunk-FH24BIIA.js +1 -0
  88. package/dist/client/assets/chunks/chunk-FNOXGAL5.js +2 -0
  89. package/dist/client/assets/chunks/chunk-FTY6WLQF.js +3 -0
  90. package/dist/client/assets/chunks/chunk-FZFME6UK.js +1 -0
  91. package/dist/client/assets/chunks/chunk-FZJZZYYO.js +1 -0
  92. package/dist/client/assets/chunks/chunk-G4Z6L7OO.js +1 -0
  93. package/dist/client/assets/chunks/chunk-GNCEAN2B.js +1 -0
  94. package/dist/client/assets/chunks/chunk-GQJIKV3T.js +1 -0
  95. package/dist/client/assets/chunks/chunk-GUMNOKT2.js +1 -0
  96. package/dist/client/assets/chunks/chunk-GXRORAF3.js +1 -0
  97. package/dist/client/assets/chunks/chunk-H36ABDKY.js +1 -0
  98. package/dist/client/assets/chunks/chunk-HAR5NYVW.js +1 -0
  99. package/dist/client/assets/chunks/chunk-HIGPS4L2.js +1 -0
  100. package/dist/client/assets/chunks/chunk-HMOBGTME.js +1 -0
  101. package/dist/client/assets/chunks/chunk-HNO6SQ7M.js +1 -0
  102. package/dist/client/assets/chunks/chunk-HYVZ3GYN.js +1 -0
  103. package/dist/client/assets/chunks/chunk-I6TGFOQS.js +1 -0
  104. package/dist/client/assets/chunks/chunk-IHIOJK7I.js +1 -0
  105. package/dist/client/assets/chunks/chunk-IHJNUMSC.js +1 -0
  106. package/dist/client/assets/chunks/chunk-IP7PB2OC.js +1 -0
  107. package/dist/client/assets/chunks/chunk-ISDTMH2Y.js +1 -0
  108. package/dist/client/assets/chunks/chunk-J65M2LNT.js +1 -0
  109. package/dist/client/assets/chunks/chunk-JCWYZ7X2.js +62 -0
  110. package/dist/client/assets/chunks/chunk-JENWYTLI.js +1 -0
  111. package/dist/client/assets/chunks/chunk-JJ6YWJQS.js +1 -0
  112. package/dist/client/assets/chunks/chunk-JMUVGGX7.js +32 -0
  113. package/dist/client/assets/chunks/chunk-JNQUFMM4.js +1 -0
  114. package/dist/client/assets/chunks/chunk-JPTECOAL.js +1 -0
  115. package/dist/client/assets/chunks/chunk-JZNK4LKS.js +1 -0
  116. package/dist/client/assets/chunks/chunk-KPNXJBIW.js +1 -0
  117. package/dist/client/assets/chunks/chunk-KZO2GZL7.js +1 -0
  118. package/dist/client/assets/chunks/chunk-L2REESOQ.js +1 -0
  119. package/dist/client/assets/chunks/chunk-LJPJXR7R.js +1 -0
  120. package/dist/client/assets/chunks/chunk-LOEZMLCI.js +1 -0
  121. package/dist/client/assets/chunks/chunk-MLYC6FSE.js +1 -0
  122. package/dist/client/assets/chunks/chunk-MMDGEZYU.js +1 -0
  123. package/dist/client/assets/chunks/chunk-MYXE7CGE.js +1 -0
  124. package/dist/client/assets/chunks/chunk-NBH7YOBD.js +15 -0
  125. package/dist/client/assets/chunks/chunk-NCOVM3YF.js +1 -0
  126. package/dist/client/assets/chunks/chunk-NCWOBVU7.js +1 -0
  127. package/dist/client/assets/chunks/chunk-NLRIHHEH.js +1 -0
  128. package/dist/client/assets/chunks/chunk-NUPEOYK6.js +1 -0
  129. package/dist/client/assets/chunks/chunk-O4MVOA7X.js +1 -0
  130. package/dist/client/assets/chunks/chunk-OBHQQZ4H.js +1 -0
  131. package/dist/client/assets/chunks/chunk-OKPNJIIR.js +1 -0
  132. package/dist/client/assets/chunks/chunk-OLMPB57R.js +1 -0
  133. package/dist/client/assets/chunks/chunk-P2FYOAEV.js +1 -0
  134. package/dist/client/assets/chunks/chunk-PI3SNP4C.js +1 -0
  135. package/dist/client/assets/chunks/chunk-PRDTEROB.js +1 -0
  136. package/dist/client/assets/chunks/chunk-PZELHD2H.js +136 -0
  137. package/dist/client/assets/chunks/chunk-QR2Q34LM.js +1 -0
  138. package/dist/client/assets/chunks/chunk-R4GIXL6S.js +1 -0
  139. package/dist/client/assets/chunks/chunk-RORF4F5V.js +1 -0
  140. package/dist/client/assets/chunks/chunk-S73BYTLL.js +1 -0
  141. package/dist/client/assets/chunks/chunk-SFC5MRYR.js +1 -0
  142. package/dist/client/assets/chunks/chunk-SVM2H22K.js +2 -0
  143. package/dist/client/assets/chunks/chunk-SZ6UCGVD.js +1 -0
  144. package/dist/client/assets/chunks/chunk-SZYM6GIJ.js +127 -0
  145. package/dist/client/assets/chunks/chunk-TT5KJZKM.js +1 -0
  146. package/dist/client/assets/chunks/chunk-UFBKUYNH.js +1 -0
  147. package/dist/client/assets/chunks/chunk-UL57AD4T.js +1 -0
  148. package/dist/client/assets/chunks/chunk-UNEI7LMM.js +1 -0
  149. package/dist/client/assets/chunks/chunk-VNQBBFIY.js +1 -0
  150. package/dist/client/assets/chunks/chunk-WGVITPII.js +1 -0
  151. package/dist/client/assets/chunks/chunk-WRA6KJHX.js +10 -0
  152. package/dist/client/assets/chunks/chunk-WS5EHW43.js +1 -0
  153. package/dist/client/assets/chunks/chunk-XE6SLBBE.js +3 -0
  154. package/dist/client/assets/chunks/chunk-XF5T2P7U.js +72 -0
  155. package/dist/client/assets/chunks/chunk-XFRY5WKO.js +1 -0
  156. package/dist/client/assets/chunks/chunk-XWYV4OPA.js +1 -0
  157. package/dist/client/assets/chunks/chunk-YCE5ECKY.js +1 -0
  158. package/dist/client/assets/chunks/chunk-YIN5CSIF.js +1 -0
  159. package/dist/client/assets/chunks/chunk-YLFUX6XH.js +1 -0
  160. package/dist/client/assets/chunks/chunk-YNXJICZM.js +1 -0
  161. package/dist/client/assets/chunks/chunk-YOHF7NGD.js +1 -0
  162. package/dist/client/assets/chunks/chunk-YORIST7Z.js +1 -0
  163. package/dist/client/assets/chunks/chunk-YUL7CG4R.js +1 -0
  164. package/dist/client/assets/chunks/chunk-YUN7JSFJ.js +2 -0
  165. package/dist/client/assets/chunks/chunk-YZ7D2IT7.js +1 -0
  166. package/dist/client/assets/chunks/chunk-Z7LAPMFJ.js +1 -0
  167. package/dist/client/assets/chunks/chunk-ZE325LPC.js +1 -0
  168. package/dist/client/assets/chunks/chunk-ZN7TN6PO.js +1 -0
  169. package/dist/client/assets/chunks/chunk-ZST3EVPO.js +1 -0
  170. package/dist/client/assets/chunks/chunk-ZZHMDDZN.js +1 -0
  171. package/dist/client/assets/chunks/clarity-RKRYQ7WF.js +1 -0
  172. package/dist/client/assets/chunks/classDiagram-v2-L25IHNXF-Y67HVDZV.js +217 -0
  173. package/dist/client/assets/chunks/clojure-2X2DQECJ.js +1 -0
  174. package/dist/client/assets/chunks/cmake-LH5524XE.js +1 -0
  175. package/dist/client/assets/chunks/cobol-7DALQF3W.js +1 -0
  176. package/dist/client/assets/chunks/codeowners-52SZU5P5.js +1 -0
  177. package/dist/client/assets/chunks/codeql-EISH3KUP.js +1 -0
  178. package/dist/client/assets/chunks/coffee-VAWSGA7Y.js +1 -0
  179. package/dist/client/assets/chunks/common-lisp-4MVK2AN6.js +1 -0
  180. package/dist/client/assets/chunks/coq-4VRYZRZV.js +1 -0
  181. package/dist/client/assets/chunks/core-BRBMZSD2.js +12 -0
  182. package/dist/client/assets/chunks/cose-bilkent-JH36ORCC-BIOOHSAA.js +1 -0
  183. package/dist/client/assets/chunks/cpp-RFECMSKD.js +1 -0
  184. package/dist/client/assets/chunks/crystal-ZBGGFAOI.js +1 -0
  185. package/dist/client/assets/chunks/csharp-BZDPMKKW.js +1 -0
  186. package/dist/client/assets/chunks/css-3WWGM6UD.js +1 -0
  187. package/dist/client/assets/chunks/csv-AB4GNWSB.js +1 -0
  188. package/dist/client/assets/chunks/cue-2E46MZP2.js +1 -0
  189. package/dist/client/assets/chunks/cynefin-EF2NZ3EQ-TLFQLWUN.js +1 -0
  190. package/dist/client/assets/chunks/cynefinDiagram-L36ESFBY-FLPXCSSG.js +62 -0
  191. package/dist/client/assets/chunks/cypher-HR3ILFOL.js +1 -0
  192. package/dist/client/assets/chunks/d-TARSZPOL.js +1 -0
  193. package/dist/client/assets/chunks/dagre-SPEB5RXH-7EGEINJ2.js +4 -0
  194. package/dist/client/assets/chunks/dart-SALLJHVI.js +1 -0
  195. package/dist/client/assets/chunks/dax-WYMRGMXC.js +1 -0
  196. package/dist/client/assets/chunks/desktop-ND5CGLLJ.js +1 -0
  197. package/dist/client/assets/chunks/diagram-3B7GL73J-6USY4HZX.js +200 -0
  198. package/dist/client/assets/chunks/diagram-DNEMPZML-BNSOC4VW.js +24 -0
  199. package/dist/client/assets/chunks/diagram-L3MUGCDN-5V5FZYIG.js +41 -0
  200. package/dist/client/assets/chunks/diagram-LA4NIPWU-25VLY2CO.js +24 -0
  201. package/dist/client/assets/chunks/diagram-MHCOC4E2-G2LFP3JU.js +3 -0
  202. package/dist/client/assets/chunks/diagram-XPFHEONP-E6HBBIBV.js +30 -0
  203. package/dist/client/assets/chunks/diff-PWYGPM7B.js +1 -0
  204. package/dist/client/assets/chunks/docker-A4D7DT7W.js +1 -0
  205. package/dist/client/assets/chunks/dotenv-VWQWV2DY.js +1 -0
  206. package/dist/client/assets/chunks/dream-maker-M5YDQS35.js +1 -0
  207. package/dist/client/assets/chunks/ebnfDiagram-UZSYDL2S-BMXXZZPI.js +1 -0
  208. package/dist/client/assets/chunks/edge-3QXIDJHI.js +1 -0
  209. package/dist/client/assets/chunks/elixir-3OX2UHIZ.js +1 -0
  210. package/dist/client/assets/chunks/elk-XV6XMXQE-IPR65FON.js +27 -0
  211. package/dist/client/assets/chunks/elm-CTS65NCT.js +1 -0
  212. package/dist/client/assets/chunks/emacs-lisp-HHS2IDO7.js +1 -0
  213. package/dist/client/assets/chunks/engine-javascript-DNHSF67B.js +141 -0
  214. package/dist/client/assets/chunks/erDiagram-EYBAPDEH-3GEHFWHG.js +99 -0
  215. package/dist/client/assets/chunks/erb-ICYWK6HS.js +1 -0
  216. package/dist/client/assets/chunks/erlang-BDQPNCKX.js +1 -0
  217. package/dist/client/assets/chunks/eventmodeling-K75KTNOO-B27BJTZL.js +1 -0
  218. package/dist/client/assets/chunks/fennel-4ONBA2PH.js +1 -0
  219. package/dist/client/assets/chunks/fish-ATLPBZSD.js +1 -0
  220. package/dist/client/assets/chunks/flowDiagram-MALGAFMO-5PCLR4AE.js +1 -0
  221. package/dist/client/assets/chunks/fluent-GGMONBVW.js +1 -0
  222. package/dist/client/assets/chunks/fortran-fixed-form-5MSBDRWQ.js +1 -0
  223. package/dist/client/assets/chunks/fortran-free-form-TT3RBL75.js +1 -0
  224. package/dist/client/assets/chunks/fsharp-PAGHNW4O.js +1 -0
  225. package/dist/client/assets/chunks/ganttDiagram-23REXITY-VE5MVBTO.js +292 -0
  226. package/dist/client/assets/chunks/gdresource-5OTP326D.js +1 -0
  227. package/dist/client/assets/chunks/gdscript-VPWHSCLF.js +1 -0
  228. package/dist/client/assets/chunks/gdshader-FYLPXFE5.js +1 -0
  229. package/dist/client/assets/chunks/genie-UYMEEMA2.js +1 -0
  230. package/dist/client/assets/chunks/gherkin-SU7D5DMY.js +1 -0
  231. package/dist/client/assets/chunks/git-commit-5CF5QAG7.js +1 -0
  232. package/dist/client/assets/chunks/git-rebase-X72BAUZI.js +1 -0
  233. package/dist/client/assets/chunks/gitGraph-VSP46ZUC-J6CJ5YZF.js +1 -0
  234. package/dist/client/assets/chunks/gitGraphDiagram-STJD7YCW-7YJGT6ZP.js +106 -0
  235. package/dist/client/assets/chunks/gleam-LR42YRKO.js +1 -0
  236. package/dist/client/assets/chunks/glimmer-js-JPF67FJU.js +1 -0
  237. package/dist/client/assets/chunks/glimmer-ts-BYPAVIZT.js +1 -0
  238. package/dist/client/assets/chunks/glsl-GTGUULBN.js +1 -0
  239. package/dist/client/assets/chunks/gn-NMDQA4K3.js +1 -0
  240. package/dist/client/assets/chunks/gnuplot-UUAMYBCZ.js +1 -0
  241. package/dist/client/assets/chunks/go-4RERSTNP.js +1 -0
  242. package/dist/client/assets/chunks/graphql-5A6RK6YF.js +1 -0
  243. package/dist/client/assets/chunks/groovy-O7BJONCU.js +1 -0
  244. package/dist/client/assets/chunks/hack-7QGTNPDP.js +1 -0
  245. package/dist/client/assets/chunks/haml-ON5GAWN4.js +1 -0
  246. package/dist/client/assets/chunks/handlebars-6UOQSUBP.js +1 -0
  247. package/dist/client/assets/chunks/haskell-OIZXZ26Y.js +1 -0
  248. package/dist/client/assets/chunks/haxe-7FFVS4FO.js +1 -0
  249. package/dist/client/assets/chunks/hcl-XFUFQJHV.js +1 -0
  250. package/dist/client/assets/chunks/hjson-QDBHMTD5.js +1 -0
  251. package/dist/client/assets/chunks/hlsl-4LP25FFN.js +1 -0
  252. package/dist/client/assets/chunks/html-7RNQXAAV.js +1 -0
  253. package/dist/client/assets/chunks/html-derivative-ESOY72Q2.js +1 -0
  254. package/dist/client/assets/chunks/http-SAD4PMBD.js +1 -0
  255. package/dist/client/assets/chunks/hurl-ZSJJXPQH.js +1 -0
  256. package/dist/client/assets/chunks/hxml-HJCC37RK.js +1 -0
  257. package/dist/client/assets/chunks/hy-CLDE3P4K.js +1 -0
  258. package/dist/client/assets/chunks/imba-REB34U5V.js +1 -0
  259. package/dist/client/assets/chunks/info-OHQRW6UA-EQQKXR77.js +1 -0
  260. package/dist/client/assets/chunks/infoDiagram-KBXO4MBG-F6KGZCGS.js +2 -0
  261. package/dist/client/assets/chunks/ini-PDFEMGRA.js +1 -0
  262. package/dist/client/assets/chunks/ishikawaDiagram-M35NCXHK-5D6ZOZ34.js +70 -0
  263. package/dist/client/assets/chunks/java-KB6XENAD.js +1 -0
  264. package/dist/client/assets/chunks/javascript-3JX5T423.js +1 -0
  265. package/dist/client/assets/chunks/jinja-V4UNU6K4.js +1 -0
  266. package/dist/client/assets/chunks/jison-KH5XNLJP.js +1 -0
  267. package/dist/client/assets/chunks/journeyDiagram-OPLOXDXO-ANDOJZKW.js +139 -0
  268. package/dist/client/assets/chunks/json-HPXFRRPY.js +1 -0
  269. package/dist/client/assets/chunks/json5-LJM6TXWM.js +1 -0
  270. package/dist/client/assets/chunks/jsonc-UZWRU3JV.js +1 -0
  271. package/dist/client/assets/chunks/jsonl-RFZD5UN4.js +1 -0
  272. package/dist/client/assets/chunks/jsonnet-GFZABBL6.js +1 -0
  273. package/dist/client/assets/chunks/jssm-2YIGBISJ.js +1 -0
  274. package/dist/client/assets/chunks/jsx-KLV2ZWUT.js +1 -0
  275. package/dist/client/assets/chunks/julia-3TZDIPPO.js +1 -0
  276. package/dist/client/assets/chunks/just-UHCONBQS.js +1 -0
  277. package/dist/client/assets/chunks/kanban-definition-BZKZHSQY-GF5I5NVD.js +89 -0
  278. package/dist/client/assets/chunks/katex-PIN72PQZ.js +257 -0
  279. package/dist/client/assets/chunks/kdl-DARTFOCL.js +1 -0
  280. package/dist/client/assets/chunks/kotlin-7KT5DW6Y.js +1 -0
  281. package/dist/client/assets/chunks/kusto-EKYXZTC5.js +1 -0
  282. package/dist/client/assets/chunks/langs-GO3UQR4H.js +1 -0
  283. package/dist/client/assets/chunks/latex-T57MIHR5.js +1 -0
  284. package/dist/client/assets/chunks/lean-5QVPRMV6.js +1 -0
  285. package/dist/client/assets/chunks/less-K6QEH5TN.js +1 -0
  286. package/dist/client/assets/chunks/liquid-3LCNNEIV.js +1 -0
  287. package/dist/client/assets/chunks/llvm-O32ZIVRF.js +1 -0
  288. package/dist/client/assets/chunks/log-C4YPJ5BB.js +1 -0
  289. package/dist/client/assets/chunks/logo-JPMO43A6.js +1 -0
  290. package/dist/client/assets/chunks/lua-YKVSOG7K.js +1 -0
  291. package/dist/client/assets/chunks/luau-K4EECYSO.js +1 -0
  292. package/dist/client/assets/chunks/make-VHSYMHHW.js +1 -0
  293. package/dist/client/assets/chunks/markdown-GC5J46H4.js +1 -0
  294. package/dist/client/assets/chunks/marko-U45YFADC.js +1 -0
  295. package/dist/client/assets/chunks/matlab-AMOKC4LU.js +1 -0
  296. package/dist/client/assets/chunks/mdc-DIGA2UXP.js +1 -0
  297. package/dist/client/assets/chunks/mdx-BZUGCKQA.js +1 -0
  298. package/dist/client/assets/chunks/mermaid-WHDP6ZPA.js +1 -0
  299. package/dist/client/assets/chunks/mermaid.core-SQMPMUDN.js +10 -0
  300. package/dist/client/assets/chunks/mindmap-definition-I2QAS2VK-4NH5S56C.js +98 -0
  301. package/dist/client/assets/chunks/mipsasm-2S4CUGQV.js +1 -0
  302. package/dist/client/assets/chunks/mojo-ZOL2PNKT.js +1 -0
  303. package/dist/client/assets/chunks/moonbit-U3BDAIFE.js +1 -0
  304. package/dist/client/assets/chunks/move-YSXALBWA.js +1 -0
  305. package/dist/client/assets/chunks/narrat-DHFIAUJH.js +1 -0
  306. package/dist/client/assets/chunks/nextflow-ESAKNTI3.js +1 -0
  307. package/dist/client/assets/chunks/nextflow-groovy-FKDSPJUI.js +1 -0
  308. package/dist/client/assets/chunks/nginx-XYAYERLO.js +1 -0
  309. package/dist/client/assets/chunks/nim-XOWBVFDH.js +1 -0
  310. package/dist/client/assets/chunks/nix-RVSTQSHC.js +1 -0
  311. package/dist/client/assets/chunks/nsis-KMDUKQRL.js +1 -0
  312. package/dist/client/assets/chunks/nushell-SKJHHZ7U.js +1 -0
  313. package/dist/client/assets/chunks/objective-c-3B3KXQ7Q.js +1 -0
  314. package/dist/client/assets/chunks/objective-cpp-6JDULPJS.js +1 -0
  315. package/dist/client/assets/chunks/ocaml-TMRFXBVO.js +1 -0
  316. package/dist/client/assets/chunks/odin-IN3PLCK7.js +1 -0
  317. package/dist/client/assets/chunks/openscad-P52IKPNH.js +1 -0
  318. package/dist/client/assets/chunks/org-N3SJ2PIA.js +1 -0
  319. package/dist/client/assets/chunks/packet-JDAUHWVQ-4SWIKSKP.js +1 -0
  320. package/dist/client/assets/chunks/pascal-T7CUWCQ4.js +1 -0
  321. package/dist/client/assets/chunks/pegDiagram-FD4XPPHP-WDKZSEGN.js +1 -0
  322. package/dist/client/assets/chunks/perl-LCZ4KODH.js +1 -0
  323. package/dist/client/assets/chunks/php-4XHMBVR5.js +1 -0
  324. package/dist/client/assets/chunks/pie-XZMESJXO-PSSWOWGV.js +1 -0
  325. package/dist/client/assets/chunks/pieDiagram-J4AKIIQI-BDW5I5ON.js +39 -0
  326. package/dist/client/assets/chunks/pkl-OACG5B67.js +1 -0
  327. package/dist/client/assets/chunks/plsql-7E5C7CJX.js +1 -0
  328. package/dist/client/assets/chunks/po-ZSREFRRP.js +1 -0
  329. package/dist/client/assets/chunks/polar-4GBINVA7.js +1 -0
  330. package/dist/client/assets/chunks/postcss-B7RPVGFW.js +1 -0
  331. package/dist/client/assets/chunks/powerquery-6UFMTY4G.js +1 -0
  332. package/dist/client/assets/chunks/powershell-3STRPCNT.js +1 -0
  333. package/dist/client/assets/chunks/prisma-4TPSPIZ5.js +1 -0
  334. package/dist/client/assets/chunks/prolog-HEAYPKQW.js +1 -0
  335. package/dist/client/assets/chunks/proto-Y3Y3VKXM.js +1 -0
  336. package/dist/client/assets/chunks/pug-7PBPMG3M.js +1 -0
  337. package/dist/client/assets/chunks/puppet-FR35R4OI.js +1 -0
  338. package/dist/client/assets/chunks/purescript-OUHXQAOW.js +1 -0
  339. package/dist/client/assets/chunks/python-SNDG7FNU.js +1 -0
  340. package/dist/client/assets/chunks/qml-UVIOSWP4.js +1 -0
  341. package/dist/client/assets/chunks/qmldir-6JGUNYL5.js +1 -0
  342. package/dist/client/assets/chunks/qss-ZCC466IZ.js +1 -0
  343. package/dist/client/assets/chunks/quadrantDiagram-XTYBLID2-5VKXWVJ6.js +7 -0
  344. package/dist/client/assets/chunks/r-4XW4O3ZF.js +1 -0
  345. package/dist/client/assets/chunks/racket-YJAYXOOG.js +1 -0
  346. package/dist/client/assets/chunks/radar-ABXABTNO-3AXZCEY2.js +1 -0
  347. package/dist/client/assets/chunks/railroad-I3PHUGI6-2PLXYPOT.js +1 -0
  348. package/dist/client/assets/chunks/railroad-abnf-LNEFI6M7-M4DGICQX.js +1 -0
  349. package/dist/client/assets/chunks/railroad-ebnf-DWYD2UWJ-UEIHQMSZ.js +1 -0
  350. package/dist/client/assets/chunks/railroad-peg-JR7BVNR7-I7VLIBXM.js +1 -0
  351. package/dist/client/assets/chunks/railroadDiagram-MRQ6NCDL-Y6IYWEFL.js +1 -0
  352. package/dist/client/assets/chunks/raku-63NEL5JK.js +1 -0
  353. package/dist/client/assets/chunks/razor-J3CJP523.js +1 -0
  354. package/dist/client/assets/chunks/rbs-6GQLT7NW.js +1 -0
  355. package/dist/client/assets/chunks/reg-6Q3OEC2C.js +1 -0
  356. package/dist/client/assets/chunks/regexp-LPIMILT3.js +1 -0
  357. package/dist/client/assets/chunks/rel-TOQGUTHB.js +1 -0
  358. package/dist/client/assets/chunks/requirementDiagram-Y7LWUY3S-CIWXVVWJ.js +84 -0
  359. package/dist/client/assets/chunks/riscv-UELGBOXE.js +1 -0
  360. package/dist/client/assets/chunks/ron-FW7LCJJH.js +1 -0
  361. package/dist/client/assets/chunks/rosmsg-O63VHE3L.js +1 -0
  362. package/dist/client/assets/chunks/rst-3WJXGLA7.js +1 -0
  363. package/dist/client/assets/chunks/ruby-7S7L6JTH.js +1 -0
  364. package/dist/client/assets/chunks/rust-LIK4HRRI.js +1 -0
  365. package/dist/client/assets/chunks/sankeyDiagram-WGJFYUXA-OGNQPX5V.js +40 -0
  366. package/dist/client/assets/chunks/sas-FHDPZ2JH.js +1 -0
  367. package/dist/client/assets/chunks/sass-UI4HW2FN.js +1 -0
  368. package/dist/client/assets/chunks/scala-KN7RQSUJ.js +1 -0
  369. package/dist/client/assets/chunks/scheme-5R527YZ5.js +1 -0
  370. package/dist/client/assets/chunks/scss-KZ6DRE4A.js +1 -0
  371. package/dist/client/assets/chunks/sdbl-FHOCL5SL.js +1 -0
  372. package/dist/client/assets/chunks/sequenceDiagram-5NDRBG5M-QX2V24ZU.js +169 -0
  373. package/dist/client/assets/chunks/shaderlab-2TLHUS2H.js +1 -0
  374. package/dist/client/assets/chunks/shellscript-PIB6ZBDM.js +1 -0
  375. package/dist/client/assets/chunks/shellsession-5KL7NFNN.js +1 -0
  376. package/dist/client/assets/chunks/sizeCapture-INFHLROL-LCS2PIHM.js +1 -0
  377. package/dist/client/assets/chunks/smalltalk-JFLFU247.js +1 -0
  378. package/dist/client/assets/chunks/smithy-HONEXZAM.js +1 -0
  379. package/dist/client/assets/chunks/solidity-SJ3INJFZ.js +1 -0
  380. package/dist/client/assets/chunks/soy-ND4SWTM2.js +1 -0
  381. package/dist/client/assets/chunks/sparql-YB2JQBZD.js +1 -0
  382. package/dist/client/assets/chunks/splunk-UXIDDAWL.js +1 -0
  383. package/dist/client/assets/chunks/sql-K6OMG7SQ.js +1 -0
  384. package/dist/client/assets/chunks/ssh-config-I7KJY3KQ.js +1 -0
  385. package/dist/client/assets/chunks/stata-DNZ3DTFJ.js +1 -0
  386. package/dist/client/assets/chunks/stateDiagram-v2-FJ4FJNEA-LVAUWF3A.js +291 -0
  387. package/dist/client/assets/chunks/stylus-GWPR6IQY.js +1 -0
  388. package/dist/client/assets/chunks/surrealql-KQNHBWLC.js +1 -0
  389. package/dist/client/assets/chunks/svelte-YQKF25S4.js +1 -0
  390. package/dist/client/assets/chunks/swift-ZCIZUJXA.js +1 -0
  391. package/dist/client/assets/chunks/swimlanes-UMQ2AFYF-B3RQOARQ.js +1 -0
  392. package/dist/client/assets/chunks/swimlanesDiagram-OMPLPG4O-JOE33EOV.js +8 -0
  393. package/dist/client/assets/chunks/system-verilog-Y7E6FIO3.js +1 -0
  394. package/dist/client/assets/chunks/systemd-WHC7NBZP.js +1 -0
  395. package/dist/client/assets/chunks/talonscript-WC7SODAH.js +1 -0
  396. package/dist/client/assets/chunks/tasl-QHY2GTCB.js +1 -0
  397. package/dist/client/assets/chunks/tcl-VAMIL5PJ.js +1 -0
  398. package/dist/client/assets/chunks/temml-ISNSX3VV.js +96 -0
  399. package/dist/client/assets/chunks/templ-B3BHKWV4.js +1 -0
  400. package/dist/client/assets/chunks/terraform-Q4UEK6FK.js +1 -0
  401. package/dist/client/assets/chunks/tex-63OWCYAB.js +1 -0
  402. package/dist/client/assets/chunks/timeline-definition-SEUHKTQE-FDFTVAJG.js +120 -0
  403. package/dist/client/assets/chunks/toml-7Z4IPGES.js +1 -0
  404. package/dist/client/assets/chunks/treeView-D4JQ5SDB-ABSGRAGI.js +1 -0
  405. package/dist/client/assets/chunks/treemap-SAJKECNS-MYX2CBJN.js +1 -0
  406. package/dist/client/assets/chunks/ts-tags-DT62HE52.js +1 -0
  407. package/dist/client/assets/chunks/tsv-RTXHWADP.js +1 -0
  408. package/dist/client/assets/chunks/tsx-UANUBOC7.js +1 -0
  409. package/dist/client/assets/chunks/turtle-QVOUNUUL.js +1 -0
  410. package/dist/client/assets/chunks/twig-ZPC4BPDA.js +1 -0
  411. package/dist/client/assets/chunks/typescript-WGWKIMMW.js +1 -0
  412. package/dist/client/assets/chunks/typespec-TYCVR32J.js +1 -0
  413. package/dist/client/assets/chunks/typst-PIBFYUO6.js +1 -0
  414. package/dist/client/assets/chunks/usecaseDiagram-AFA6YXN4-KW7ADINR.js +370 -0
  415. package/dist/client/assets/chunks/v-3ERNWBSP.js +1 -0
  416. package/dist/client/assets/chunks/vala-UCBCT2GS.js +1 -0
  417. package/dist/client/assets/chunks/vb-DGI3VTPA.js +1 -0
  418. package/dist/client/assets/chunks/vennDiagram-NNMJR3RB-RDGLGOGM.js +34 -0
  419. package/dist/client/assets/chunks/verilog-G72NZROS.js +1 -0
  420. package/dist/client/assets/chunks/vhdl-PYGA77XE.js +1 -0
  421. package/dist/client/assets/chunks/viml-OOC5B5UU.js +1 -0
  422. package/dist/client/assets/chunks/vue-GTNHIOPG.js +1 -0
  423. package/dist/client/assets/chunks/vue-html-DKAMJV55.js +1 -0
  424. package/dist/client/assets/chunks/vue-vine-WTM5HREV.js +1 -0
  425. package/dist/client/assets/chunks/vyper-RHYEHOJM.js +1 -0
  426. package/dist/client/assets/chunks/wardley-7MLQ67FV-QJL2HSBT.js +1 -0
  427. package/dist/client/assets/chunks/wardleyDiagram-S7YFUSEH-A4XX3UJR.js +78 -0
  428. package/dist/client/assets/chunks/wasm-ZMM2Z5XV.js +1 -0
  429. package/dist/client/assets/chunks/wenyan-HF5UTKSJ.js +1 -0
  430. package/dist/client/assets/chunks/wgsl-O7D7OPO7.js +1 -0
  431. package/dist/client/assets/chunks/wikitext-T7BSWSKQ.js +1 -0
  432. package/dist/client/assets/chunks/wit-TKCHPPT6.js +1 -0
  433. package/dist/client/assets/chunks/wolfram-7MN66A7N.js +1 -0
  434. package/dist/client/assets/chunks/xml-NLTLAWOD.js +1 -0
  435. package/dist/client/assets/chunks/xsl-NZ4ECK3P.js +1 -0
  436. package/dist/client/assets/chunks/xychartDiagram-GGNKY7IX-OXJILWHR.js +7 -0
  437. package/dist/client/assets/chunks/yaml-HRJLEV3V.js +1 -0
  438. package/dist/client/assets/chunks/zenscript-4LLRSLPI.js +1 -0
  439. package/dist/client/assets/chunks/zig-Y7MUQFUO.js +1 -0
  440. package/dist/client/assets/icon.svg +1 -0
  441. package/dist/client/assets/main.js +254 -0
  442. package/dist/client/index.html +59 -0
  443. package/dist/diff.js +119 -0
  444. package/dist/fs/files.js +233 -0
  445. package/dist/fs/git.js +369 -0
  446. package/dist/fs/glob.js +89 -0
  447. package/dist/fs/notes.js +368 -0
  448. package/dist/fs/search.js +136 -0
  449. package/dist/fs/watch.js +69 -0
  450. package/dist/fs/zip.js +100 -0
  451. package/dist/hooks.js +65 -0
  452. package/dist/http.js +81 -0
  453. package/dist/hub/atomic.js +77 -0
  454. package/dist/hub/notify.js +239 -0
  455. package/dist/hub/registry.js +219 -0
  456. package/dist/mcp.js +500 -0
  457. package/dist/q/agent.js +215 -0
  458. package/dist/q/model.js +633 -0
  459. package/dist/q/schema.js +133 -0
  460. package/dist/q/store.js +413 -0
  461. package/dist/r/model.js +323 -0
  462. package/dist/r/schema.js +53 -0
  463. package/dist/r/store.js +489 -0
  464. package/dist/server.js +246 -0
  465. package/dist/settings.js +30 -0
  466. package/dist/workspace.js +639 -0
  467. package/package.json +73 -4
  468. package/schema/questions.schema.json +1670 -0
  469. package/schema/reviews.schema.json +151 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 ClickerMonkey
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,219 @@
1
- # Temporary Holding Version
1
+ # hitl — Human In The Loop
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ **hitl** is where an AI agent and its human meet. The agent **asks** you questions, **shows** you things, asks you to
4
+ **review** its work, and waits at a **gate** for your OK. You answer, review, mark up pictures and browse the project's
5
+ files, on your phone or your computer. Everything you say lands in one feed the agent reads a little at a time.
6
+
7
+ The npm package is **`hitloop`**; once installed (`npm i -g hitloop`) the command is `hitl`.
8
+
9
+ ```sh
10
+ npx hitloop init # once per project: the .hitl folder, the MCP server for Claude Code, a note in CLAUDE.md
11
+ npx hitloop # the page, for every project, with links for your phone
12
+ ```
13
+
14
+ **Site: https://clickermonkey.github.io/hitloop/** (the commands, and a Claude skill to copy or download).
15
+
16
+ | | | | |
17
+ |:-:|:-:|:-:|:-:|
18
+ | ![The inbox: an approval waiting, a review, questions](docs/screens/phone-inbox.jpg) | ![A review: a line comment and the agent's reply](docs/screens/phone-review.jpg) | ![Before and after: a slider over two screenshots](docs/screens/phone-compare.jpg) | ![An approval: Refuse, Not yet, Approve](docs/screens/phone-approve.jpg) |
19
+ | **Inbox** | **Review** | **Before / after** | **Approve** |
20
+
21
+ "Human in the loop" means a person stays part of what an automated system does: it checks with them, they decide,
22
+ and it carries on. hitl gives that person somewhere to do it, instead of a chat scrolling past.
23
+
24
+ It grew from two smaller tools, and does everything both did:
25
+
26
+ - [questionme](https://github.com/ClickerMonkey/questionme): an agent's questions, answered on your phone.
27
+ - [folderview](https://github.com/ClickerMonkey/folderview): browse, search and comment on any folder, with git.
28
+
29
+ ## What you can do
30
+
31
+ | | |
32
+ |---|---|
33
+ | **Inbox** | Everything waiting on you across every project, most urgent first: approvals (an agent is waiting), blocking questions, reviews, things to look at. What's done folds away. |
34
+ | **Answer questions** | Dozens at a time, with recommendations, why each one matters, and pictures, diffs and mockups to decide from. Take the recommendations and change the few you disagree with. Text answers grow and wrap as you type. |
35
+ | **Review** | Each thing the agent shows you gets **Looks good**, **Needs changes** or **No**. Comment on a line (tap its number; tap another for a range), on selected text, or on an **area of a picture** you draw around. Attach photos. Finish with **Approve** or **Request changes**. |
36
+ | **One at a time** | Swipe through a review: right for looks good, left for needs changes. Good for thirty screenshots. |
37
+ | **Before and after** | Pictures with a slider (or side by side); code and text as after, before, and the changes between them. |
38
+ | **Approve** | An agent asks "Deploy 2.4.0 to production?" and waits. You get a notification, read what it will do, and tap **Approve** or **Refuse**. |
39
+ | **Browse the files** | Everything folderview does: highlighted code, rendered markdown with Mermaid and math, images, CSV, JSON, notebooks, git status, diffs and history, search, downloads. Big files and big folders open quickly. |
40
+ | **Notes for the agent** | Write on any file, line or picture area, or about anything, with photos from your phone. The agent picks them up and resolves each with a reply. |
41
+ | **Decisions** | Everything you answered, approved or gave a verdict on, searchable. Agents check it before asking again. |
42
+ | **Who's asking** | Each request says which agent session or branch made it. |
43
+ | **Notifications** | Your phone buzzes when an agent asks, shows or waits (ntfy or a webhook). Approvals go out at once. |
44
+
45
+ It works on phones (sections along the bottom, big touch targets, the page scrolls), tablets and desktops (the file
46
+ tree, the folder and the file side by side).
47
+
48
+ ## For agents
49
+
50
+ `npx hitloop init` adds the MCP server to Claude Code (`claude mcp add --scope user hitl -- npx -y hitloop@latest mcp`) and
51
+ a short section to the project's `CLAUDE.md` (or `AGENTS.md`) on when to use it.
52
+
53
+ ### MCP tools
54
+
55
+ | Tool | |
56
+ |---|---|
57
+ | `news` | Everything the human said since you last read, across every question set, review and note, one line each. Start here. |
58
+ | `wait` | Block until the human says something (`any`), finishes (`done`), answers everything (`all`), or answers the blocking questions. Up to 15 minutes; for longer, run `npx hitloop wait` in the background. |
59
+ | `show` | Put files, code, text, diffs, pictures, diagrams, mockups or links in front of the human. `mode: "review"` asks a verdict per item; `"show"` is just to look. Each call makes its own review. |
60
+ | `approve` | Ask yes or no before doing something, and wait for it. Returns APPROVED, REFUSED or "not yet" with their note. |
61
+ | `add_questions` | Questions in a set (questionme's format): options, a recommendation, why it matters, follow-ups that open on an answer. |
62
+ | `get_feedback`, `get_answers`, `notes` | One review's feedback, one set's answers, the notes. |
63
+ | `ack`, `resolve`, `reply` | Mark answers and verdicts as dealt with, resolve comments and notes saying what you did, write back. |
64
+ | `decisions` | What the human decided before. |
65
+ | `status`, `get_link` | What's open; the links to send. |
66
+ | `update_item`, `remove_items`, `update_review`, `update_question`, `remove_questions`, `update_set`, `update_section`, `show_set` | Change things. |
67
+
68
+ ### The CLI does the same
69
+
70
+ ```sh
71
+ npx hitloop news # what's new (marks it read; --peek doesn't)
72
+ npx hitloop wait --any # exit once the human says something, printing it
73
+ npx hitloop wait --done <review> # exit once they've finished a review (or answered an approval)
74
+ npx hitloop show src/login.ts screens/*.png --title "Login, round 2" # a review of files
75
+ git diff | npx hitloop show - --title "The change" # stdin: a diff, code or text
76
+ npx hitloop approve "Deploy 2.4.0?" --details "Restarts the API" --timeout 30m && ./deploy.sh
77
+ npx hitloop ask <set> --file questions.json # questions (or write .hitl/questions/<set>.json)
78
+ npx hitloop feedback <review> npx hitloop answers <set> --new npx hitloop notes --open
79
+ npx hitloop ack <set|review> <id…> --note "Done in src/x.ts"
80
+ npx hitloop resolve [<review>] <id…> --note "Fixed: …"
81
+ npx hitloop reply <review> c12 "Because the API bills per call."
82
+ npx hitloop decisions auth # what was decided about auth
83
+ npx hitloop status # every request, with counts
84
+ ```
85
+
86
+ `hitl approve` exits 0 when approved, 1 when refused (or changes are asked for), 2 when there's no answer yet, so a
87
+ script can stop at a real yes or no.
88
+
89
+ `npx hitloop --help` lists every command and option; a mistyped option is refused rather than ignored.
90
+
91
+ ### Claude Code hooks (optional)
92
+
93
+ Off unless you turn them on, per project (`hitl hooks on`, `hitl hooks on --stop`, or Settings on the page):
94
+
95
+ - **News**: whenever you send Claude a message, it's told if you've said something in hitl it hasn't read.
96
+ - **Stop**: Claude can't finish while an approval or a blocking question is waiting, or while there's news it
97
+ hasn't read. (It only holds Claude once, so it can't loop.)
98
+
99
+ ## Where things live
100
+
101
+ ```
102
+ <project>/.hitl/
103
+ questions/<id>.json question sets the agent writes
104
+ reviews/<id>.json reviews, showings and approvals the agent writes
105
+ answers/<id>.json, .md what you said about each one hitl writes
106
+ notes/notes.json, NOTES.md, images/, photos/ hitl writes
107
+ media/ pictures passed as data, snapshots of shown files, marked-up pictures, photos
108
+ config.json optional: exclude, hide, showSecrets, maxFileMb, git, watch for this project
109
+ ```
110
+
111
+ Every request is its own file, and a new review always gets a fresh id, so one agent's request never replaces
112
+ another's. Every write is locked and atomic, so the page, the MCP server and the CLI can all write at once.
113
+ Formats: `npx hitloop schema questions`, `npx hitloop schema reviews` (or `schema/` in the package).
114
+
115
+ `npx hitloop import` brings in a project's `.questionme/` (questions and answers) and `.review/` (folderview's comments,
116
+ as notes).
117
+
118
+ ### A review
119
+
120
+ ```json
121
+ {
122
+ "title": "Login screen, round 2",
123
+ "intro": "I moved the button and fixed the spacing. **The error text** is the part I'm least sure of.",
124
+ "mode": "review",
125
+ "from": "Claude · branch login-v2",
126
+ "items": [
127
+ { "file": "src/login.tsx", "note": "The form", "ask": "Is the error text clear?" },
128
+ { "file": "src/api.ts", "against": "main", "title": "What changed in the API" },
129
+ { "image": "screens/login.png", "before": "screens/login-old.png" },
130
+ { "code": "{ \"retries\": 3 }", "before": "{}", "lang": "json", "name": "config.json" },
131
+ { "diff": "--- a/x\n+++ b/x\n@@ …" },
132
+ { "md": "## Left out\n- Password reset" },
133
+ { "mermaid": "flowchart LR\n A --> B" },
134
+ { "html": "<button>Sign in</button>", "height": 120 },
135
+ { "link": "https://example.com/preview" }
136
+ ]
137
+ }
138
+ ```
139
+
140
+ A `file` item keeps a copy of the file as it was shown; if it changes later, the page says so and shows what changed.
141
+
142
+ ## One server for every project
143
+
144
+ `npx hitloop` starts one shared server (the hub) on port 4199 for every project you've used it in, so your phone needs
145
+ one bookmark. The MCP server starts it in the background when an agent needs a link.
146
+
147
+ ```sh
148
+ npx hitloop # the hub (adds this folder); prints the links
149
+ npx hitloop url [review|set] # the links (starts the hub if needed)
150
+ npx hitloop stop # stop the hub
151
+ npx hitloop projects # what it serves; --forget <folder> to drop one
152
+ npx hitloop --solo [folder] # just this folder, on its own server
153
+ ```
154
+
155
+ | Option | Environment | Default | |
156
+ |---|---|---|---|
157
+ | `-p, --port <n>` | `HITL_PORT` | `4199` | The port to try first; the next free one is used. |
158
+ | `-H, --host <addr>` | `HITL_HOST` | `0.0.0.0` | Where to listen. The default lets your phone reach it. |
159
+ | `--base <path>` | `HITL_BASE` | `/` | Serve under a URL path (behind a proxy). |
160
+ | `--local` | | | Only this computer (`127.0.0.1`), no token. |
161
+ | `--token <t>` | `HITL_TOKEN` | made once | Open a link with `?token=…` once on each device; it's kept as a cookie. |
162
+ | `--no-token` | | | Anyone on your network can open it. |
163
+ | `--allow-host <h>` | | | Accept this host name too (a tunnel's). |
164
+ | `-x, --exclude <globs>` | | | Never show, serve, search or zip these. Also `.hitlignore` in a project. |
165
+ | `--hide <names>`, `--show-secrets`, `--max-file-mb <n>`, `--no-git`, `--no-watch`, `--author <name>` | | | File browsing, as in folderview. |
166
+ | `-o, --open` | | | Open it in your browser. |
167
+ | `--read-only` | | | Look, don't answer. |
168
+
169
+ Settings can also go in `~/.config/hitl/config.json` (`port`, `host`, `base`, `token`, `allowHosts`, `notify`,
170
+ `exclude`, `hide`, `showSecrets`, `maxFileMb`, `git`, `watch`). `HITL_HOME` moves that folder.
171
+
172
+ The links include every address your phone might use: your Wi-Fi or LAN address, and a Tailscale address if you have
173
+ one. Add the page to your phone's home screen and it opens like an app.
174
+
175
+ ## Notifications on your phone
176
+
177
+ ```sh
178
+ npx hitloop notify ntfy # makes a private ntfy topic, saves it, and sends a test
179
+ npx hitloop notify webhook <url> # or POST JSON anywhere: Pushover, Discord, Home Assistant, …
180
+ npx hitloop notify link http://100.100.4.74:4199 # where the tap should open
181
+ npx hitloop notify test | off
182
+ ```
183
+
184
+ Install the [ntfy app](https://ntfy.sh) and subscribe to the topic it prints. Questions and reviews are batched (one
185
+ notification once the agent has been quiet for 20 seconds); approvals go out within a couple of seconds, at high
186
+ priority. Notifications say what's waiting, never your answers.
187
+
188
+ ## Safety
189
+
190
+ - **A token by default**: the server listens on your network so your phone can reach it, so every request needs the
191
+ token (in the link once, then a cookie). `--local` listens on this computer only.
192
+ - **Project files** are served only from inside the project, never `.git` or secret-looking files (`.env`, `*.key`,
193
+ `*.pem`, `id_rsa`), never through a link that points outside, and never excluded ones.
194
+ - **What an agent writes is untrusted**: markdown is sanitised, HTML runs in a sandbox with no scripts, SVGs and files
195
+ are served so they can't run script, diagrams are drawn as images.
196
+ - **DNS rebinding** is refused (unknown host names), and so are writes from other sites.
197
+ - **It writes only** in `.hitl/` and its own config folder; `init` also writes `CLAUDE.md`, and the hooks
198
+ `.claude/settings.json`.
199
+
200
+ ## Development
201
+
202
+ ```sh
203
+ npm install
204
+ npm test # builds, then runs the tests
205
+ node scripts/demo.mjs .demo/trailhead # a demo project with everything in it
206
+ HITL_HOME=.demo/home node dist/cli.js --solo .demo/trailhead --local
207
+ node scripts/shots.mjs http://localhost:4199/ .demo/shots # phone and desktop screenshots of every page
208
+ ```
209
+
210
+ - **The server** is TypeScript in `src/`, no runtime dependencies, Node 20 or newer.
211
+ - **The page** is plain JavaScript in `client/`, bundled by esbuild; Shiki's grammars, Mermaid and the math renderer
212
+ load only when needed.
213
+
214
+ Releasing: bump the version in `package.json`, commit, push to main. The Release workflow publishes a version that
215
+ isn't on npm yet (trusted publishing), tags it and makes a GitHub release.
216
+
217
+ ## License
218
+
219
+ MIT
package/dist/agent.js ADDED
@@ -0,0 +1,318 @@
1
+ // What an agent reads: one feed of everything the human said since it last read (answers, verdicts, comments, notes),
2
+ // compact so hundreds of answers don't flood its context; waiting until the human has said something; and the
3
+ // decision log. Shared by the MCP tools and the CLI.
4
+ import path from 'node:path';
5
+ import { Questions, StoreError } from './q/store.js';
6
+ import { readAnswers, summaryLine as questionsLine } from './q/agent.js';
7
+ import { allQuestions, answerText, isDone } from './q/model.js';
8
+ import { Reviews } from './r/store.js';
9
+ import { VERDICT_TEXT, isStale, summarise, verdictText, whereText } from './r/model.js';
10
+ import { Notes } from './fs/notes.js';
11
+ export function openProject(root) {
12
+ const r = path.resolve(root);
13
+ return { root: r, questions: new Questions(r), reviews: new Reviews(r), notes: new Notes(path.join(r, '.hitl', 'notes'), r) };
14
+ }
15
+ // ── reviews ──
16
+ export function reviewLine(s) {
17
+ const bits = [];
18
+ if (s.mode === 'approve')
19
+ bits.push(s.overall ? (s.overall === 'ok' ? 'APPROVED' : s.overall === 'no' ? 'REFUSED' : 'changes asked for') : 'waiting for a yes or no');
20
+ else {
21
+ if (s.mode === 'review')
22
+ bits.push(`${s.reviewed} of ${s.items} items reviewed`);
23
+ else
24
+ bits.push(`${s.items} item${s.items === 1 ? '' : 's'} shown`);
25
+ if (s.ok)
26
+ bits.push(`${s.ok} good`);
27
+ if (s.changes)
28
+ bits.push(`${s.changes} need changes`);
29
+ if (s.no)
30
+ bits.push(`${s.no} no`);
31
+ if (s.open)
32
+ bits.push(`${s.open} open comment${s.open === 1 ? '' : 's'}`);
33
+ if (s.overall)
34
+ bits.push(`overall: ${VERDICT_TEXT[s.overall]}`);
35
+ }
36
+ if (s.stale)
37
+ bits.push(`${s.stale} verdict${s.stale === 1 ? '' : 's'} on an earlier version`);
38
+ if (s.unread)
39
+ bits.push(`${s.unread} new`);
40
+ if (s.status === 'closed')
41
+ bits.push('closed');
42
+ if (s.errors)
43
+ bits.push(`${s.errors} error${s.errors === 1 ? '' : 's'} in the file`);
44
+ return `${s.id} (${s.mode}, "${s.title}"${s.from ? `, from ${s.from}` : ''}): ${bits.join(', ')}`;
45
+ }
46
+ export const REVIEW_FILTERS = ['new', 'all', 'open'];
47
+ /** The human's feedback on a review, as text and data. "new" marks it read unless peek. */
48
+ export function readFeedback(reviews, id, opts = {}) {
49
+ const l = reviews.load(id);
50
+ if (!l)
51
+ throw new StoreError(`No review "${id}"${reviews.ids().length ? ` (there are: ${reviews.ids().join(', ')})` : ''}`);
52
+ const fb = reviews.feedback(id);
53
+ const r = l.review;
54
+ const filter = opts.filter ?? 'new';
55
+ const since = filter === 'new' ? fb.readSeq : -1;
56
+ const s = summarise(r, fb, l.errors.length);
57
+ const lines = [reviewLine(s)];
58
+ if (fb.overall && fb.overall.seq > since)
59
+ lines.push(`- OVERALL: ${verdictText(r, fb.overall.verdict).toUpperCase()}${fb.overall.note ? ` — "${fb.overall.note}"` : ''}`);
60
+ const label = (iid) => {
61
+ const it = r.items.find((i) => i.id === iid);
62
+ return iid === '' ? '(the whole review)' : it ? `${iid} (${it.title})` : `${iid} (removed)`;
63
+ };
64
+ for (const it of r.items) {
65
+ const v = fb.items[it.id];
66
+ if (v && v.seq > since)
67
+ lines.push(`- ${label(it.id)}: ${VERDICT_TEXT[v.verdict]}${v.note ? ` — "${v.note}"` : ''}${isStale(it, v) ? ' (given to an earlier version)' : ''}${v.ack && filter !== 'new' ? ` [done: ${v.ack.note}]` : ''}`);
68
+ }
69
+ const show = (c) => {
70
+ const fresh = c.seq > since || c.replies.some((x) => x.seq > since && x.from === 'owner');
71
+ if (filter === 'open')
72
+ return c.status === 'open' && c.from === 'owner';
73
+ return fresh;
74
+ };
75
+ for (const c of fb.comments.filter(show)) {
76
+ const w = whereText(c);
77
+ lines.push(`- comment ${c.id} on ${label(c.item)}${w ? `, ${w}` : ''}${c.status === 'resolved' ? ' [resolved]' : ''}: "${c.body}"`);
78
+ if (c.quote)
79
+ lines.push(` about: ${JSON.stringify(c.quote.length > 300 ? c.quote.slice(0, 297) + '…' : c.quote)}`);
80
+ if (c.marked)
81
+ lines.push(` marked-up picture: ${c.marked}`);
82
+ for (const p of c.photos ?? [])
83
+ lines.push(` photo: ${p}`);
84
+ for (const x of c.replies)
85
+ if (filter !== 'new' || (x.seq > since && x.from === 'owner'))
86
+ lines.push(` ${x.from === 'owner' ? 'human' : 'you'}: "${x.body}"`);
87
+ }
88
+ if (lines.length === 1)
89
+ lines.push(filter === 'new' ? 'Nothing new since you last read.' : filter === 'open' ? 'No open comments.' : 'No feedback yet.');
90
+ if (filter === 'new' && !opts.peek && fb.seq > fb.readSeq)
91
+ reviews.markRead(id, fb.seq);
92
+ return { text: lines.join('\n'), data: { summary: s, feedback: fb } };
93
+ }
94
+ // ── notes ──
95
+ export function readNotes(notes, opts = {}) {
96
+ const { seq, readSeq } = notes.marks();
97
+ const filter = opts.filter ?? 'new';
98
+ const list = notes.all().filter((c) => {
99
+ if (filter === 'open')
100
+ return c.status === 'open';
101
+ if (filter === 'all')
102
+ return true;
103
+ return (c.author !== 'agent' && (c.seq ?? 0) > readSeq) || (c.replies ?? []).some((r) => r.from !== 'agent' && (r.seq ?? 0) > readSeq);
104
+ });
105
+ const lines = [];
106
+ for (const c of list) {
107
+ const where = !c.path ? 'a note' : c.line ? `${c.path}:${c.line}${c.endLine ? '-' + c.endLine : ''}` : c.area ? `${c.path} (marked area ${c.area.w}×${c.area.h} at ${c.area.x},${c.area.y})` : c.path;
108
+ lines.push(`- note ${c.id} on ${where}${c.status === 'resolved' ? ' [resolved]' : ''}: "${c.body}"`);
109
+ if (c.quote)
110
+ lines.push(` lines: ${JSON.stringify(c.quote.length > 300 ? c.quote.slice(0, 297) + '…' : c.quote)}`);
111
+ if (c.image)
112
+ lines.push(` marked-up picture: .hitl/notes/${c.image}`);
113
+ for (const p of c.photos ?? [])
114
+ lines.push(` photo: .hitl/notes/${p}`);
115
+ for (const r of c.replies ?? [])
116
+ if (filter !== 'new' || ((r.seq ?? 0) > readSeq && r.from !== 'agent' && r.author !== 'agent'))
117
+ lines.push(` ${r.from === 'agent' || r.author === 'agent' ? 'you' : 'human'}: "${r.body}"`);
118
+ }
119
+ if (filter === 'new' && !opts.peek && seq > readSeq)
120
+ notes.markRead(seq);
121
+ return { text: lines.join('\n'), count: list.length };
122
+ }
123
+ /** How much is new for the agent, per kind (cheap: nothing is marked read). */
124
+ export function newsCounts(p) {
125
+ let questions = 0, reviews = 0;
126
+ for (const s of p.questions.summaries())
127
+ questions += s.unread + s.unreadMessages;
128
+ for (const s of p.reviews.summaries())
129
+ reviews += s.unread;
130
+ const notes = p.notes.unread();
131
+ return { questions, reviews, notes, total: questions + reviews + notes };
132
+ }
133
+ /** Everything the human said since the agent last read, across every question set, review and note. */
134
+ export function news(p, opts = {}) {
135
+ const counts = newsCounts(p);
136
+ const out = [];
137
+ const want = (id) => !opts.ids?.length || opts.ids.includes(id);
138
+ for (const s of p.reviews.summaries()) {
139
+ if (!s.unread || !want(s.id))
140
+ continue;
141
+ out.push(`## Review ${readFeedback(p.reviews, s.id, { filter: 'new', peek: opts.peek }).text}`, '');
142
+ }
143
+ for (const s of p.questions.summaries()) {
144
+ if (!(s.unread + s.unreadMessages) || !want(s.id))
145
+ continue;
146
+ out.push(`## Questions ${readAnswers(p.questions, s.id, { filter: 'new', peek: opts.peek }).text}`, '');
147
+ }
148
+ if (counts.notes && want('notes')) {
149
+ const n = readNotes(p.notes, { filter: 'new', peek: opts.peek });
150
+ if (n.count)
151
+ out.push(`## Notes from the human (${n.count}; resolve each with a reply saying what you did)`, n.text, '');
152
+ }
153
+ if (!out.length)
154
+ out.push('Nothing new from the human since you last read.');
155
+ return { text: out.join('\n').trimEnd(), counts };
156
+ }
157
+ /** What's open, briefly: for `hitl status` and the MCP list tool. */
158
+ export function overviewText(p) {
159
+ const lines = [];
160
+ const reviews = p.reviews.summaries();
161
+ const sets = p.questions.summaries();
162
+ const notes = p.notes.all();
163
+ if (reviews.length)
164
+ lines.push('Reviews:', ...reviews.map((s) => '- ' + reviewLine(s)));
165
+ if (sets.length)
166
+ lines.push('Question sets:', ...sets.map((s) => '- ' + questionsLine(s)));
167
+ const open = notes.filter((c) => c.status === 'open').length;
168
+ if (notes.length)
169
+ lines.push(`Notes from the human: ${open} open of ${notes.length}${p.notes.unread() ? `, ${p.notes.unread()} new` : ''}`);
170
+ if (!lines.length)
171
+ lines.push(`Nothing in ${p.root} yet: no questions, reviews or notes.`);
172
+ const c = newsCounts(p);
173
+ if (c.total)
174
+ lines.push('', `New since you last read: ${c.total} (npx hitloop news, or the news tool).`);
175
+ return lines.join('\n');
176
+ }
177
+ // ── waiting ──
178
+ export const WAITS = ['any', 'done', 'all', 'blocking'];
179
+ function met(p, w) {
180
+ const until = w.until ?? 'any';
181
+ const ids = w.ids?.length ? w.ids : undefined;
182
+ const stamp = [];
183
+ if (until === 'any') {
184
+ let n = 0;
185
+ for (const s of p.reviews.summaries())
186
+ if (!ids || ids.includes(s.id))
187
+ n += s.unread;
188
+ for (const s of p.questions.summaries())
189
+ if (!ids || ids.includes(s.id))
190
+ n += s.unread + s.unreadMessages;
191
+ if (!ids || ids.includes('notes'))
192
+ n += p.notes.unread();
193
+ return { ok: n > 0, stamp: String(n) + ':' + seqs(p, ids) };
194
+ }
195
+ const targets = ids ?? [...p.reviews.ids().filter((id) => p.reviews.summary(id)?.status === 'open'), ...p.questions.ids()];
196
+ if (!targets.length)
197
+ throw new StoreError('Nothing to wait for: name a review or question set');
198
+ let ok = true;
199
+ for (const id of targets) {
200
+ if (p.reviews.has(id)) {
201
+ const s = p.reviews.summary(id);
202
+ if (!s)
203
+ continue;
204
+ stamp.push(p.reviews.feedback(id).seq);
205
+ if (until === 'done' || until === 'blocking')
206
+ ok &&= s.done;
207
+ else
208
+ ok &&= s.done || (s.mode !== 'approve' && s.reviewed >= s.items && s.items > 0);
209
+ }
210
+ else if (p.questions.has(id)) {
211
+ const s = p.questions.summary(id);
212
+ if (!s)
213
+ continue;
214
+ stamp.push(p.questions.answers(id).seq);
215
+ if (until === 'blocking')
216
+ ok &&= s.blocking === 0;
217
+ else
218
+ ok &&= s.toAnswer === 0;
219
+ }
220
+ else
221
+ throw new StoreError(`No review or question set "${id}"`);
222
+ }
223
+ return { ok, stamp: stamp.join(',') };
224
+ }
225
+ function seqs(p, ids) {
226
+ const out = [];
227
+ for (const id of p.reviews.ids())
228
+ if (!ids || ids.includes(id))
229
+ out.push(p.reviews.feedback(id).seq);
230
+ for (const id of p.questions.ids())
231
+ if (!ids || ids.includes(id))
232
+ out.push(p.questions.answers(id).seq);
233
+ out.push(p.notes.marks().seq);
234
+ return out.join(',');
235
+ }
236
+ /** Wait until the condition holds (and the human has paused). Resolves true, or false on timeout or abort. */
237
+ export async function waitFor(p, w) {
238
+ const started = Date.now();
239
+ const poll = w.pollMs ?? 1000;
240
+ let metAt = 0;
241
+ let lastStamp = '';
242
+ for (;;) {
243
+ if (w.signal?.aborted)
244
+ return false;
245
+ const m = met(p, w);
246
+ if (m.ok) {
247
+ if (!w.settleMs)
248
+ return true;
249
+ if (m.stamp !== lastStamp) {
250
+ lastStamp = m.stamp;
251
+ metAt = Date.now();
252
+ }
253
+ else if (Date.now() - metAt >= w.settleMs)
254
+ return true;
255
+ }
256
+ else
257
+ lastStamp = '';
258
+ if (w.timeoutMs && Date.now() - started >= w.timeoutMs)
259
+ return false;
260
+ await new Promise((r) => setTimeout(r, poll));
261
+ }
262
+ }
263
+ /** What the human decided: every answer, verdict and approval, newest first. q narrows it (words, all must match). */
264
+ export function decisions(p, opts = {}) {
265
+ const out = [];
266
+ for (const id of p.questions.ids()) {
267
+ const l = p.questions.load(id);
268
+ if (!l)
269
+ continue;
270
+ const af = p.questions.answers(id);
271
+ for (const q of allQuestions(l.set)) {
272
+ const a = af.answers[q.id];
273
+ if (!a || !isDone(a) || a.status === 'skipped')
274
+ continue;
275
+ out.push({ at: a.at, kind: 'answer', request: id, requestTitle: l.set.title, id: q.id, subject: q.question, decision: answerText(q, a), ...(a.note ? { note: a.note } : {}) });
276
+ }
277
+ }
278
+ for (const id of p.reviews.ids()) {
279
+ const l = p.reviews.load(id);
280
+ if (!l)
281
+ continue;
282
+ const fb = p.reviews.feedback(id);
283
+ const r = l.review;
284
+ if (fb.overall)
285
+ out.push({ at: fb.overall.at, kind: r.mode === 'approve' ? 'approval' : 'overall', request: id, requestTitle: r.title, id: '', subject: r.title, decision: verdictText(r, fb.overall.verdict), ...(fb.overall.note ? { note: fb.overall.note } : {}), ...(r.from ? { from: r.from } : {}) });
286
+ for (const it of r.items) {
287
+ const v = fb.items[it.id];
288
+ if (v)
289
+ out.push({ at: v.at, kind: 'verdict', request: id, requestTitle: r.title, id: it.id, subject: it.title, decision: VERDICT_TEXT[v.verdict], ...(v.note ? { note: v.note } : {}), ...(r.from ? { from: r.from } : {}) });
290
+ }
291
+ }
292
+ out.sort((a, b) => b.at.localeCompare(a.at));
293
+ const words = (opts.q ?? '').toLowerCase().split(/\s+/).filter(Boolean);
294
+ const hits = words.length ? out.filter((d) => words.every((w) => `${d.requestTitle} ${d.subject} ${d.decision} ${d.note ?? ''} ${d.request} ${d.id}`.toLowerCase().includes(w))) : out;
295
+ return { decisions: hits.slice(0, Math.max(1, Math.min(2000, opts.limit ?? 200))), total: hits.length };
296
+ }
297
+ export function decisionsText(p, opts = {}) {
298
+ const { decisions: list, total } = decisions(p, opts);
299
+ if (!list.length)
300
+ return opts.q ? `No decisions match "${opts.q}".` : 'No decisions yet.';
301
+ const lines = [`${total} decision${total === 1 ? '' : 's'}${opts.q ? ` matching "${opts.q}"` : ''}${total > list.length ? ` (the newest ${list.length})` : ''}:`];
302
+ for (const d of list)
303
+ lines.push(`- ${d.at.slice(0, 10)} ${d.request}${d.id ? '/' + d.id : ''} — ${short(d.subject)}: ${d.decision}${d.note ? ` ("${d.note}")` : ''}`);
304
+ return lines.join('\n');
305
+ }
306
+ const short = (s) => {
307
+ const t = s.replace(/\s+/g, ' ').trim();
308
+ return t.length > 90 ? t.slice(0, 87) + '…' : t;
309
+ };
310
+ export function parseDuration(s, fallback) {
311
+ if (s === undefined || s === '')
312
+ return fallback;
313
+ const m = /^(\d+(?:\.\d+)?)\s*(ms|s|m|h)?$/.exec(s.trim());
314
+ if (!m)
315
+ throw new StoreError(`Not a duration: "${s}" (like 90s, 10m, 2h)`);
316
+ const n = Number(m[1]);
317
+ return Math.round(n * ({ ms: 1, s: 1000, m: 60_000, h: 3_600_000 }[m[2] ?? 's'] ?? 1000));
318
+ }